package simulator.types;

import net.minidev.json.JSONObject;
import simulator.entities.BaseVehicle;
import simulator.entities.Bike;
import simulator.entities.Bus;
import simulator.entities.Car;
import simulator.entities.Pedestrian;
import simulator.entities.TrafficLane;
import simulator.enums.Direction;
import simulator.enums.EntityType;
import simulator.enums.LaneType;
import simulator.main.Simulator;

public class VehicleInput implements Comparable<VehicleInput>
{
	private static final String JSON_TIME_FIELD = "time";
	private static final String JSON_TYPE_FIELD = "type";
	private static final String JSON_FROM_FIELD = "from";
	private static final String JSON_TO_FIELD = "to";
	
	double _time;
	
	EntityType _type;
	
	Lane _spawnLane;
	Lane _destLane;
	
	Simulator _parent;
	
	
	/**
	 * Builds VehicleInput from JSONObject. Throws illegal argument exception if the JSONObject does not have all required fields.
	 * @param data
	 */
	public VehicleInput(JSONObject data, Simulator s)
	{
		_parent = s;
		
		
		if(!validateJSON(data))
		{
			throw new IllegalArgumentException("Could not create VehicleInput from JSON: JSONObject does not describe VehicleInput");
		}
		
		if(!parseJSON(data))
		{
			throw new IllegalArgumentException("JSON object contains illegal data");
		}
	}
	
	public double getTime()
	{
		return _time;
	}
	
	public EntityType getType()
	{
		return _type;
	}
	
	public Lane getSpawn()
	{
		return _spawnLane;
	}
	
	public Lane getDestination()
	{
		return _destLane;
	}
	
	public static boolean validateJSON(JSONObject data)
	{
		boolean valid = true;
		valid &= data.containsKey(JSON_TIME_FIELD);
		valid &= data.containsKey(JSON_TYPE_FIELD);
		valid &= data.containsKey(JSON_FROM_FIELD);
		valid &= data.containsKey(JSON_TO_FIELD);
		return valid;
	}
	
	private boolean parseJSON(JSONObject data)
	{
		Object obj = data.get(JSON_TIME_FIELD);
		
		if(obj instanceof Integer)
		{
			Integer i = (Integer) obj;
			_time = i;
		}
		else if(obj instanceof Double)
		{
			_time = (Double) obj;
		}
		else
		{
			return false;
		}
		
		if(_time < 0) return false;
		
		obj = data.get(JSON_TYPE_FIELD);
		if(obj instanceof String)
		{
			String typ = (String) obj;
			
			_type = EntityType.fromString(typ);
			
			if(_type == null) return false;
		}
		else
		{
			return false;
		}
		
		obj = data.get(JSON_FROM_FIELD);
		if(obj instanceof String)
		{
			String from = (String) obj;
			
			char dr = from.charAt(0);
			Direction dir = Direction.getFromChar(dr);
			if(dir == null) return false;
			
			String n = from.substring(1);
			int num = 0;
			try
			{
				num = Integer.valueOf(n);
			}
			catch(NumberFormatException e)
			{
				return false;
			}
			
			LaneType type = LaneType.fromID(num);
			
			if(type == null) return false;
			
			_spawnLane = new Lane(dir, type);
		}
		else
		{
			return false;
		}
		
		obj = data.get(JSON_TO_FIELD);
		if(obj instanceof String)
		{
			String from = (String) obj;
			
			char dr = from.charAt(0);
			Direction dir = Direction.getFromChar(dr);
			if(dir == null) return false;
			
			String n = from.substring(1);
			int num = 0;
			try
			{
				num = Integer.valueOf(n);
			}
			catch(NumberFormatException e)
			{
				return false;
			}
			
			LaneType type = LaneType.fromID(num);
			
			if(type == null) return false;
			
			_destLane = new Lane(dir, type);
		}
		else
		{
			return false;
		}
		
		return true;
	}

	@Override
	public int compareTo(VehicleInput o)
	{
		Double d = _time;
		Double d_o = o._time;
		return d.compareTo(d_o);
	}
	
	public BaseVehicle createEntity()
	{
		TrafficLane s = _parent.getLane(_spawnLane.toString());
		TrafficLane d = _parent.getLane(_destLane.toString());
		
		switch(_type)
		{
			case BIKE:
				return new Bike(s, d);
			case BUS:
				return new Bus(s, d);
			case CAR:
				return new Car(s, d);
			case PED:
				return new Pedestrian(s, d);
			default:
				throw new RuntimeException("Should not be executed: default case in filled enum switch");
		}
	}
}